#ifndef __MINGW32__
  #include <string.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <stddef.h>
  #include <unistd.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>
#else
  #include <process.h>
  #include <winsock2.h>
  #pragma comment(lib,"ws2_32.lib")
#endif
#include <pthread.h>
#include "skyeye_uart.h"

#define PORT                34567
#define BACKLOG             5
#define BUFFER_SIZE         256

int ready,listenfd,thread_exit;
pthread_mutex_t mutex;
typedef struct SEQ_QUEUE{
  char value[BUFFER_SIZE];
  int front;
  int rear;
}seq_queue;
seq_queue input_queue,output_queue;

static void clear_queue(seq_queue *q){
  pthread_mutex_lock(&mutex);
  q->front = q->rear = 0;
  memset(q->value,0,BUFFER_SIZE);
  pthread_mutex_unlock(&mutex);
}

static int insert_queue(seq_queue *q,char ch){
  if ((q->rear+1)%BUFFER_SIZE == q->front) return -1;

  pthread_mutex_lock(&mutex);
  q->value[q->rear] = ch;
  q->rear = (q->rear+1) % BUFFER_SIZE;
  //printf("ins: f:%d r:%d c:%c\n", q->front, q->rear, ch);
  pthread_mutex_unlock(&mutex);

  return 0;
}

static int leave_queue(seq_queue *q,char *ch){
  if (q->front == q->rear) return -1;

  pthread_mutex_lock(&mutex);
  *ch = q->value[q->front];
  q->value[q->front] = 0;
  q->front = (q->front+1) % BUFFER_SIZE;
  //printf("lea: f:%d r:%d c:%c\n", q->front, q->rear, *ch);
  pthread_mutex_unlock(&mutex);

  return 0;
}

#ifdef __MINGW32__
  static void socket_thread(void *param){
#else
  static void* socket_thread(void *param){
#endif
  fd_set all_set,read_set,write_set;
  struct timeval tv;
  int tlistenfd,connectfd,maxfd,maxi,i,client[FD_SETSIZE],sockfd,nready,len,ret;
  struct sockaddr_in sin;
  char buff[BUFFER_SIZE],ch;

  maxfd = (tlistenfd = *(int*)param);
  maxi = -1;
  for (i=0; i<FD_SETSIZE; i++)
    client[i] = -1;
  FD_ZERO(&all_set);
  FD_SET(tlistenfd,&all_set);
  tv.tv_sec = 0;
  tv.tv_usec = 10000;
  for (;;){
	FD_ZERO(&read_set);
	FD_ZERO(&write_set);
    read_set = all_set;
	write_set = all_set;
    nready = select(maxfd+1,&read_set,&write_set,NULL,&tv);
	switch (nready){
	case -1:
	  goto EXIT;
	case 0:
	  if (thread_exit == 1) goto EXIT;
	  break;
	default:
	  if (FD_ISSET(tlistenfd,&read_set)){
	    len = sizeof(sin);
	    if ((connectfd=accept(tlistenfd,(struct sockaddr*)&sin,&len)) == -1)
		  continue;
        clear_queue(&output_queue);
        clear_queue(&input_queue);

	    for (i=0; i<FD_SETSIZE; i++){
		  if (client[i] < 0){
		    client[i] = connectfd;
		    break;
		  }
		}
	    if (i == FD_SETSIZE) continue;
	    FD_SET(connectfd,&all_set);
	    if (connectfd > maxfd)
		  maxfd = connectfd;
	    if (i > maxi)
		  maxi = i;
	    if (--nready <= 0) continue;
	  }
      for (i=0; i<=maxi; i++){
        if ((sockfd=client[i]) < 0) continue;
        if (FD_ISSET(sockfd,&read_set)){
          ret = recv(sockfd,buff,sizeof(buff)-1,0);
          if (ret > 0){
	          if (ready != 0){
                for (i=0; i<ret; i++)
                  insert_queue(&input_queue,buff[i]);
			  }
		  }
          else{
#ifdef __MINGW32__
           closesocket(sockfd);
#else
           close(sockfd);
#endif
            FD_CLR(sockfd,&all_set);
            client[i] = -1;
		  }
          if (--nready <= 0) break;
		}
	    if (FD_ISSET(sockfd,&write_set)){
          if (ready != 0){
		    while (output_queue.front != output_queue.rear){
			  leave_queue(&output_queue,&ch);
			  send(sockfd,&ch,1,0);
            }
	      }
		  if (--nready <= 0) break;
		}
	  }
	  break;
	}
  }
EXIT:
#ifdef __MINGW32__
  return;
#else
  pthread_exit(0);
  return 0;
#endif
}

int uart_net_init(){
#ifdef __MINGW32__
  WSADATA wsadata;
#else
  pthread_t tid;
#endif
  struct sockaddr_in sin;
  int optval;

  pthread_mutex_init(&mutex,NULL);

  #ifdef __MINGW32__
  if (WSAStartup(MAKEWORD(2,2),&wsadata) != 0)
	  return -1;
#endif

  if ((listenfd=socket(AF_INET,SOCK_STREAM,0)) == -1){
    perror("socket() failed");
    return -1;
  }

  optval = 1;
  setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&optval,sizeof(optval));

  memset(&sin,0,sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(PORT);
  sin.sin_addr.s_addr = inet_addr("127.0.0.1");
  if (bind(listenfd,(struct sockaddr*)&sin,sizeof(sin)) == -1){
    perror("bind() failed");
    return -1;
  }
  listen(listenfd,BACKLOG);

  clear_queue(&input_queue);
  clear_queue(&output_queue);

#ifdef __MINGW32__
  _beginthread(socket_thread,0,&listenfd);
#else
  pthread_create(&tid,NULL,socket_thread,&listenfd);
#endif

  return 0;
}

int uart_net_open(struct uart_device *uart_dev){
  if (ready) return -1;
  ready = 1;

  return 0;
}

int uart_net_close(struct uart_device *uart_dev){
  if (!ready) return -1;
  ready = 0;

  return 0;
}

int uart_net_read(struct uart_device *uart_dev,void *buf,size_t count,struct timeval *timeout){
  size_t i;
  char ch;
  char *ptr;

  memset(buf,0,count);
  ptr = (char*)buf;
  for (i=0; i<count; i++){
    if (leave_queue(&input_queue,&ch) != 0)
	  break;
	ptr[i] = ch;
  }

  return count;
}

int uart_net_write(struct uart_device *uart_dev,void *buf,size_t count){
  size_t i;
  char *ptr;

  ptr = (char*)buf;
  for (i=0; i<count; i++) {
    insert_queue(&output_queue,ptr[i]);
    //printf("write: %c\n", ptr[i]);
  }

  return count;
}

int uart_net_deinit(){
  thread_exit = 1;
#ifdef __MINGW32__
  closesocket(listenfd);
  WSACleanup();
#else
  close(listenfd);
#endif

  return 0;
}
